Skip to Content

通信过程

  1. 配置服务器:当Spring Boot应用程序启动时,它会自动配置嵌入式的Tomcat(或Jetty、Undertow等其他服务器)
  2. 控制访问:使用控制器来处理HTTP请求。通常用@RequestMapping注解来映射HTTP请求到相应的处理方法
  3. 处理请求:服务器接收到请求后,解析其中的请求参数,根据URL路由到相应的控制器方法,并进行初步数据验证。控制器会将请求转发给业务逻辑层相应的服务(Service)进行处理
  4. 返回响应:控制器方法处理完请求后,会返回一个模型和视图(ModelAndView),或者直接返回一个对象,Spring Boot会自动将这个对象转换为JSON或XML等格式,然后服务器将这个响应发送回浏览器

控制访问

功能

  • 将不同HTTP请求映射到不同处理方法

实现

  • @RequestMapping(properties)
    • value:指定请求的实际地址
    • method:指定请求的HTTP方法类型,如GET、POST、PUT、DELETE等。默认情况下,不指定method属性时 @RequestMapping会处理所有HTTP方法
    • consumes:指定处理请求的提交内容类型(Content-Type),例如application/json、application/xml等
    • produces:指定返回的内容类型,仅当请求头中的Accept类型中包含该指定类型才返回
    • params:指定请求中必须包含某些参数值
    • headers:指定请求中必须包含某些指定的header值
@RequestMapping(value = "/path", method = RequestMethod.GET) public responseType handlerMethod() { // ... }
  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping

处理请求

功能

  • 接受前端HTTP请求,在后端进行处理,并返回需要的数据

实现

  • @RestController:组合注解,相当于下面两个
  • @Controller:定义控制器,被自动注册为Spring的Bean,用于处理HTTP请求并生成响应,可以返回任何类型的数据,如字符串、模型对象、视图名称
  • @ResponseBody

请求参数

功能

  • 从HTTP请求中获取传入的参数值,以进行数据处理

实现

  • @RequestParam(properties):从HTTP请求中获取参数值,并将这些值绑定到控制器方法的参数上
    • value:请求的参数值
    • required:该请求值是否必须
    • defaultValue:如果没有该参数将使用默认值
@Controller public class GreetingController { @GetMapping("/greet") @ResponseBody public String greetUser(@RequestParam(value = "name", required = false, defaultValue = "World") String name) { return "Hello, " + name + "!"; } }
  • @RequestBody:将HTTP请求体中的JSON或XML格式数据自动转化为对象实例
@RequestMapping("/jsonParam") public String jsonParam(@RequestBody User user){ System.out.println(user); return "OK"; }

细节

  • 对于GET请求,由于没有请求体,所以不能使用 @RequestBody
  • @RequestBody 会将请求体中的JSON或XML格式数据转化为对象实例,这个过程需要依赖Jackson库
  • 在使用 @RequestBody 时,一定要确保参数类型与请求体中的数据类型相符,否则可能会出现数据解析错误的问题
  • 日期参数需要用注解@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")指定日期格式
//http://localhost:8080/dateParam?updateTime=2002-11-11 00:00:00 @RequestMapping("/dateParam") public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime) { System.out.println(updateTime); return "OK"; }
  • 路径参数需要用注解@PathVariable接收
@RequestMapping("/path/{id}/{name}") public String pathParam(@PathVariable Integer id,@PathVariable String name) { System.out.println(id+":"+name); return "OK"; }

数据验证

请求格式验证

请求实体

  • 使用@Valid@Validated注解结合JSR 380(Bean Validation 2.0)提供的注解(如@NotNull@Size@Pattern等)对传入的请求实体(DTO)进行验证
// 请求实体定义 @Data public class UserRegistrationDTO { @NotBlank(message = "用户名不能为空") // 确保字段不为空,如果字段为空,则会抛出验证异常,并显示相应的错误消息 private String username; @NotBlank(message = "密码不能为空") @Size(min = 8, message = "密码长度不能少于8位") // 确保密码的长度至少为8位 private String password; @NotBlank(message = "邮箱不能为空") @Email(message = "邮箱格式不正确") // 确保邮箱字段符合邮箱的格式 private String email; @Pattern(regexp = "^[0-9]{11}$", message = "手机号码格式不正确") // 验证手机号码是否符合特定的正则表达式 private String phoneNumber; } // 控制器定义 @RestController public class UserController { @PostMapping("/register") public ResponseEntity<String> registerUser(@Validated @RequestBody UserRegistrationDTO userRegistrationDTO) { // 如果验证通过,继续处理注册逻辑 return ResponseEntity.ok("用户注册成功"); } }

请求参数

  • 使用@Min@Max@DecimalMin@DecimalMax等注解对请求参数进行数值范围的校验
// 控制器定义 @RestController public class ProductController { @GetMapping("/products") public String getProducts( @RequestParam @Min(0) @Max(1000) int price) { return "查询到的商品价格范围是: " + price; } @GetMapping("/advancedProducts") public String getAdvancedProducts( @RequestParam @DecimalMin("0.00") @DecimalMax("1000.00") double price) { return "查询到的高级商品价格范围是: " + price; } }

权限验证

  • 使用Spring Security等安全框架进行用户身份验证和授权
  • 通过配置安全规则,可以确保只有具有相应权限的用户才能访问特定的API
  • 在控制器方法上使用@PreAuthorize@Secured等注解来定义方法级别的安全约束
@RestController @RequestMapping("/books") public class BookController { // 允许所有用户访问 @GetMapping public List<Book> listBooks() { // 获取书籍列表 return bookService.getBooks(); } // 使用 @PreAuthorize 注解 @DeleteMapping("/{id}") @PreAuthorize("hasRole('ROLE_ADMIN')") // 只有具有ROLE_ADMIN角色的用户可以访问 public void deleteBook(@PathVariable Long id) { // 删除书籍 bookService.deleteBook(id); } // 使用 @Secured 注解 @PutMapping("/{id}") @Secured("ROLE_ADMIN") //只有具有ROLE_ADMIN角色的用户可以访问 public Book updateBook(@PathVariable Long id, @RequestBody Book book) { // 更新书籍 return bookService.updateBook(id, book); } }

请求方法验证

  • 使用@RequestMapping@GetMapping@PostMapping等注解确保请求方法(GET、POST、PUT、DELETE等)与控制器方法定义相匹配

跨域请求验证

  • 使用@CrossOrigin注解,处理局部控制器跨域请求
@RestController public class MyController { // 允许来自 http://example.com 的跨域请求 @CrossOrigin(origins = "http://example.com") @GetMapping("/api/data") public String getData() { return "This is some data"; } }
  • 在配置类中使用WebMvcConfigurer接口设置全局的跨域处理规则,通常在config包中进行配置
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") // 应用到所有的URL .allowedOrigins("http://example.com") // 允许的域 .allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的方法 .allowedHeaders("*") // 允许的头信息 .allowCredentials(true) // 是否允许证书(cookies) .maxAge(3600); // 预检请求的缓存时间(秒) } }

自定义验证

  1. 创建自定义注解
    • @Constraint:定义这个用于验证的注解将由什么验证类来进行验证
    • @Target:定义注解应用对象
    • @Retention:定义注解保留阶段
    • @interface:声明这是一个注解接口
@Constraint(validatedBy = UsernameValidator.class) // 指定自定义注解将由UsernameValidator类来验证。 @Target({ ElementType.FIELD, ElementType.METHOD }) //指定自定义注解可以应用于字段(FIELD)和方法(METHOD) @Retention(RetentionPolicy.RUNTIME) // 指定自定义注解保留到运行时 public @interface ValidUsername { // 声明了一个公共的注解接口ValidUsername String message() default "Invalid username"; // 定义验证失败时的默认错误消息 Class<?>[] groups() default {}; // 定义这个注解属于哪些验证组 Class<? extends Payload>[] payload() default {}; // 定义这个注解携带哪些载荷,载荷可以用于进一步指定验证失败时的行为 }
  1. 创建验证器类
public class UsernameValidator implements ConstraintValidator<ValidUsername, String> { @Override public void initialize(ValidUsername constraintAnnotation) { // 设置验证器需要的任何状态或配置 // 初始化操作(如果有需要) } @Override // value是需要验证的字符串,context是上下文对象,可以用来传递验证失败时的错误信息 public boolean isValid(String value, ConstraintValidatorContext context) { // 自定义验证逻辑:这里只验证用户名是否为 "admin" return value != null && value.equalsIgnoreCase("admin"); } }
  1. 在模型类中使用自定义注解
@Data public class User { @NotNull private String name; @ValidUsername // 使用自定义的注解 private String username; }
  1. 在控制器中触发验证
@RestController public class UserController { @PostMapping("/createUser") public String createUser(@Valid @RequestBody User user) { // 如果 username 不是 "admin",会抛出 ConstraintViolationException return "User created successfully"; } }

响应数据

功能

  • 将处理好的数据封装成需要的格式并返回给前端

实现

JSON格式

  • 基于JavaScript对象的表示法,语法简洁,但扩展性相对较弱,结构较为固定
  • 使用 @ResponseBody 返回对象,这些对象会被自动转换成 JSON 格式
@RestController public class MyController { @GetMapping("/api/users") public List<User> getUsers() { return userService.findAll(); } }

XML格式

  • 采用自定义标签的形式,结构较为冗长,但具有良好的扩展性,可以通过自定义标签和属性灵活表示复杂数据
  • 使用 @RequestMapping 注解,并设置 produces 属性为 “application/xml”
@RequestMapping(value = "/api/users", produces = "application/xml") public List<User> getUsers() { return userService.findAll(); }

CSV格式

  • 用于存储表格数据,如电子表格或数据库,但并不存储数据类型
  • 每一行是一个数据记录,字段通过逗号分隔(或其他字符,如制表符、分号等)
  • CSV文件是纯文本文件,格式简单,便于跨平台
  • 使用 @RequestMapping 注解,并设置 produces 属性为 “text/csv”
@ResponseBody @RequestMapping(value = "/api/users/csv", produces = "text/csv") public ResponseEntity<String> downloadCsv() { String csvData = convertListToCsv(userService.findAll()); return ResponseEntity // 创建了一个 ResponseEntity 对象 // 表示HTTP状态码200(OK),告诉前端请求成功 .ok() // 设置了HTTP响应头Content-Disposition,告诉浏览器这是一个附件,并且建议的文件名为users.csv .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=users.csv") // 设置了HTTP响应的正文,即之前转换得到的CSV格式的字符串数据。 .body(csvData); }

自定义格式

功能
  • 使用ResponseEntity类,在控制器方法中封装 HTTP 响应的状态码、头信息和响应体,灵活地控制 HTTP 响应的内容
实现
  • 封装响应数据:JSON、XML、CSV、String …
  • 设置HTTP状态码:200 OK、404 Not Found、500 Internal Server Error …
  • 设置响应头:内容类型(Content-Type)、缓存控制(Cache-Control)、跨域请求(CORS)…
// 通过构造器返回 public ResponseEntity(T body, HttpStatus status, HttpHeaders headers) //构造函数 @GetMapping("/headers") public ResponseEntity<String> getWithHeaders() { HttpHeaders headers = new HttpHeaders(); headers.add("Custom-Header", "HeaderValue"); return new ResponseEntity<>("Response with custom header", headers, HttpStatus.OK); } // 通过链式调用返回 (每个方法都会返回 ResponseEntity.BodyBuilder 本身) @GetMapping("/download") public ResponseEntity<String> downloadCsv() { String csvData = "name,age,email\nAlice,30,alice@example.com\nBob,25,bob@example.com"; return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=users.csv") .body(csvData); }
细节
  • ResponseEntity类位于org.springframework.http包中

统一响应封装

功能
  • 确保前端和后端之间的交互加便捷和统一,便于前端预期后端返回的数据结构,从而简化前端逻辑处理,增强系统的稳定性和可维护性
实现
  • 封装Result实体类
@Data @NoArgsConstructor @AllArgsConstructor public class ApiResponse<T> { /** * 响应状态码 * 200:成功 * 400:客户端请求错误 * 500:服务器内部错误 */ private int code; /** * 响应消息 */ private String message; /** * 响应数据 */ private T data; /** * 分页信息 */ private Pagination pagination; /** * 构造函数,用于成功的响应 * @param data 响应数据 */ public ApiResponse(T data) { this.code = 200; this.message = "Success"; this.data = data; } /** * 构造函数,用于错误的响应 * @param message 错误消息 */ public ApiResponse(String message) { this.code = 500; this.message = message; } /** * 分页信息类 */ @Data public static class Pagination { private int page; // 当前页码 private int size; // 每页大小 private long total; // 总记录数 private int totalPages;// 总页数 public Pagination(int page, int size, long total) { this.page = page; this.size = size; this.total = total; this.totalPages = (int) Math.ceil((double) total / size); } } /** * 创建成功的响应 * @param data 响应数据 * @return ApiResponse实例 */ public static <T> ApiResponse<T> success(T data) { return new ApiResponse<>(data); } /** * 创建成功的响应,包含分页信息 * @param data 响应数据 * @param page 当前页码 * @param size 每页大小 * @param total 总记录数 * @return ApiResponse实例 */ public static <T> ApiResponse<T> success(T data, int page, int size, long total) { ApiResponse<T> response = new ApiResponse<>(data); response.setPagination(new Pagination(page, size, total)); return response; } /** * 创建错误的响应 * @param message 错误消息 * @return ApiResponse实例 */ public static <T> ApiResponse<T> error(String message) { return new ApiResponse<>(message); } }
Last updated on